# Copyright (C) 2025 Huawei Technologies Co., Ltd. All rights reserved.	

<# NOTE: openUBMC User Module Cmdlets #>

function Add-openUBMCUser {
<#
.SYNOPSIS
Add a new openUBMC user account.

.DESCRIPTION
Add a new openUBMC user account. The session user must have privilege to add new user.

.PARAMETER Session
openUBMC redfish session object which is created by Connect-openUBMC cmdlet.
A session object identifies an openUBMC server to which this cmdlet will be executed.

.PARAMETER Username
Username specifies the new username to be added.
A string of 1 to 16 characters is allowed. It can contain letters, digits, and special characters (excluding <>&,'"/\%), but cannot contain spaces or start with a number sign (#).

.PARAMETER Password
Password specifies the password of this new add user.
A string of 1 to 20 characters is allowed.
- If password complexity check is enabled for other interfaces, the password must meet password complexity requirements.
- If password complexity check is not enabled for other interfaces, there is not restriction on the password

.PARAMETER Role
Role specifies the role of this new add user.
Available role value set is:
- "Administrator"
- "Operator"
- "Commonuser"
- "Noaccess"
- "CustomRole1"
- "CustomRole2"
- "CustomRole3"
- "CustomRole4"

.PARAMETER SecureEnabled
switch to turn on GUI for retrieving account username and password.
Note: this features does not provide different username and password for different servers.

.OUTPUTS
PSObject[]
Returns the new created User object array if cmdlet executes successfully.
In case of an error or warning, exception will be returned.

.EXAMPLE

Create a new user with name "new-user" for a single openUBMC server

PS C:\> $credential = Get-Credential
PS C:\> $session = Connect-openUBMC -Address 192.168.1.1 -Credential $credential -TrustCert
PS C:\> $pwd = ConvertTo-SecureString -String new-user-password -AsPlainText -Force
PS C:\> $User = Add-openUBMCUser -Session $session -Username new-user -Password $pwd -Role Operator
PS C:\> $User

Host     : 192.168.1.1
Id       : 12
Name     : User Account
UserName : new-user
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.EXAMPLE

Create a new user with name "new-user" for a single openUBMC server with pipelined session


PS C:\> $credential = Get-Credential
PS C:\> $session = Connect-openUBMC -Address 192.168.10.2 -Credential $credential -TrustCert
PS C:\> $pwd = ConvertTo-SecureString -String new-user-password -AsPlainText -Force
PS C:\> ,$session | Add-openUBMCUser -Username new-user -Password $pwd -Role Operator

Host     : 192.168.10.2
Id       : 12
Name     : User Account
UserName : new-user
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.EXAMPLE

Create a new user with name "new-user" for a single openUBMC server with SecureEnabled


PS C:\> $credential = Get-Credential
PS C:\> $session = Connect-openUBMC -Address 192.168.10.2 -Credential $credential -TrustCert
PS C:\> Add-openUBMCUser -Session $session -Role Commonuser -SecureEnabled

Host     : 192.168.10.2
Id       : 6
Name     : User Account
UserName : new-user
RoleId   : Commonuser
Locked   : False
Enabled  : True
Oem      : @{openUBMC=}

.EXAMPLE

Create a new user with name "new-user" for multiple openUBMC servers with pipelined session

PS C:\> $credential = Get-Credential
PS C:\> $sessions = Connect-openUBMC -Address 192.168.10.2-3 -Credential $credential -TrustCert
PS C:\> $pwd = ConvertTo-SecureString -String new-user-password -AsPlainText -Force
PS C:\> ,$sessions | Add-openUBMCUser -Username new-user,new-user2 -Password $pwd,$pwd -Role Operator,Administrator

Host     : 192.168.10.2
Id       : 12
Name     : User Account
UserName : new-user
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

Host     : 192.168.10.3
Id       : 12
Name     : User Account
UserName : new-user
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.LINK
https://gitcode.com/openUBMC/cmdlets_plugin

Get-openUBMCUser
Set-openUBMCUser
Remove-openUBMCUser
Connect-openUBMC
Disconnect-openUBMC
#>
  [CmdletBinding()]
  param (
    [RedfishSession[]]
    [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position=0)]
    $Session,

    [string[]]
    [parameter(Mandatory = $false, Position=1)]
    $Username,

    [System.Object[]]
    [parameter(Mandatory = $false, Position=2)]
    $Password,

    [UserRole[]]
    [parameter(Mandatory = $true, Position=3)]
    $Role,

    [switch]
    [parameter(Mandatory = $false)]
    $SecureEnabled
  )

  begin {
  }

  process {
    Assert-ArrayNotNull $Session 'Session'
    if ($SecureEnabled) {
      $Account = Get-AccountInfo  
      $Username = @($Account[0])
      $Password = @($Account[1])
    } else {
      Assert-ArrayNotNull $Username 'Username'
      Assert-ArrayNotNull $Password 'Password'
    }
    Assert-ArrayNotNull $Role 'Role'

    $RoleList = Get-MatchedSizeArray $Session $Role 'Session' 'Role'
    $UsernameList = Get-OptionalMatchedSizeArray $Session $Username
    $PasswordList = Get-OptionalMatchedSizeArray $Session $Password

    $AddUserBlock = {
      param($Session, $Username, $SecurePasswd, $Role)
      $Plain = ConvertTo-PlainString $SecurePasswd "Password"
      $Payload = @{
        'UserName' = "$Username";
        'Password' = "$Plain";
        'RoleId' = $Role;
      } | Resolve-EnumValues

      $Clone = $Payload.clone()
      $Clone.Password = "******"
      $Logger.info($(Trace-Session $Session "Sending payload: $($Clone | ConvertTo-Json)"))
      $response = Invoke-RedfishRequest $Session '/AccountService/Accounts' 'Post' $Payload | ConvertFrom-WebResponse
      # $response = Invoke-RedfishRequest $Session '/AccountService/Accounts' 'Post' $payload -ContinueEvenFailed

      $Properties = @("Id", "Name", "UserName", "RoleId", "Locked", "Enabled", "Oem")
      $User = Copy-ObjectProperties $Response $Properties
      return $(Update-SessionAddress $Session $User)
    }
    try {
      $tasks = New-Object System.Collections.ArrayList
      $pool = New-RunspacePool $Session.Count
      for ($idx=0; $idx -lt $Session.Count; $idx++) {
        $Parameters = @($Session[$idx], $UsernameList[$idx], $PasswordList[$idx], $RoleList[$idx])
        [Void] $tasks.Add($(Start-ScriptBlockThread $pool $AddUserBlock $Parameters))
      }
      return Get-AsyncTaskResults -AsyncTasks $tasks
    } finally {
      Close-Pool $pool
    }
  }

  end {
  }
}


function Get-openUBMCUser {
<#
.SYNOPSIS
Get all openUBMC user account details

.DESCRIPTION
Get all openUBMC user account information, excluding password.

.PARAMETER Session
openUBMC redfish session object which is created by Connect-openUBMC cmdlet.
A session object identifies an openUBMC server to which this cmdlet will be executed.


.OUTPUTS
Array[PSObject[]]
Returns array of the user object array if cmdlet executes successfully.
In case of an error or warning, exception will be returned.

.EXAMPLE

Get all user account infomation for multiple openUBMC servers


PS C:\> $credential = Get-Credential
PS C:\> $sessions = Connect-openUBMC -Address 192.168.1.1-3 -Credential $credential -TrustCert
PS C:\> $Users = Get-openUBMCUser -Session $sessions
PS C:\> $Users

Host     : 192.168.1.1
Id       : 2
Name     : User Account
UserName : Administrator
RoleId   : Administrator
Locked   : False
Enabled  : True
Oem      : @{openUBMC=}

Host     : 192.168.1.1
Id       : 3
Name     : User Account
UserName : root
RoleId   : Administrator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

Host     : 192.168.1.1
Id       : 4
Name     : User Account
UserName : zxh
RoleId   : Administrator
Locked   : False
Enabled  : True
Oem      : @{openUBMC=}


.LINK
https://gitcode.com/openUBMC/cmdlets_plugin

Add-openUBMCUser
Set-openUBMCUser
Remove-openUBMCUser
Connect-openUBMC
Disconnect-openUBMC

#>
  param (
    [RedfishSession[]]
    [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position=0)]
    $Session
  )

  begin {
  }

  process {
    Assert-ArrayNotNull $Session 'Session'
    $GetUserBlock = {
      param($Session)
      $Users = New-Object System.Collections.ArrayList
      $response = Invoke-RedfishRequest $Session '/AccountService/Accounts' | ConvertFrom-WebResponse
      $Properties = @("Id", "Name", "UserName", "RoleId", "Locked", "Enabled", "Oem")
      $response.Members | ForEach-Object {
        $UserResponse = Invoke-RedfishRequest $session $_.'@odata.id' | ConvertFrom-WebResponse
        $User = Copy-ObjectProperties $UserResponse $Properties
        [Void] $Users.Add($(Update-SessionAddress $Session $User))
      }
      return ,$Users.ToArray()
    }

    try {
      $tasks = New-Object System.Collections.ArrayList
      $pool = New-RunspacePool $Session.Count
      for ($idx=0; $idx -lt $Session.Count; $idx++) {
        [Void] $tasks.Add($(Start-ScriptBlockThread $pool $GetUserBlock @($Session[$idx])))
      }
      return Get-AsyncTaskResults -AsyncTasks $tasks
    } finally {
      Close-Pool $pool
    }
  }

  end {
  }
}

function Set-openUBMCUser {
<#
.SYNOPSIS
Modify an existing openUBMC user account's infomation.

.DESCRIPTION
Modify an existing openUBMC user account's infomation. The NewUsername parameter must not exists in all user accounts.

Modify the following properties of a user:
- User name
- Password
- Rights
- Lockout status
- Whether the user is enabled

.PARAMETER Session
openUBMC redfish session object which is created by Connect-openUBMC cmdlet.
A session object identifies an openUBMC server to which this cmdlet will be executed.

.PARAMETER Username
Username specifies the user to be modified.

.PARAMETER NewUsername
NewUsername specifies the new username of the modified user.
A string of 1 to 16 characters is allowed. It can contain letters, digits, and special characters (excluding <>&,'"/\%), but cannot contain spaces or start with a number sign (#).

.PARAMETER NewPassword
NewPassword specifies the new password of the modified user.

A string of 1 to 20 characters is allowed.
- If password complexity check is enabled for other interfaces, the password must meet password complexity requirements.
- If password complexity check is not enabled for other interfaces, there is not restriction on the password

.PARAMETER NewRole
NewRole specifies the new role of the modified user.
Available role value set is:
- "Administrator"
- "Operator"
- "Commonuser"
- "Noaccess"
- "CustomRole1"
- "CustomRole2"
- "CustomRole3"
- "CustomRole4"

.PARAMETER Enabled
Enabled specifies Whether the user is enabled. A power shell bool($true|$false) value is accept.

.PARAMETER Unlocked
If this switch parameter is $true then the modified user's lockout status is set to false.
If this switch parameter is $false then lockout status will not be modified.

.PARAMETER SecureEnabled
switch to turn on GUI for retrieving account username and password.
Note: this features does not provide different username and password for different servers.

.OUTPUTS
PSObject[]
Returns the modified User object array if cmdlet executes successfully.
In case of an error or warning, exception will be returned.

.EXAMPLE

Create a user account with name powershell and then modify "username", "password", "role", "Enabled", "Locked" properties of this user for a single openUBMC server

PS C:\> $credential = Get-Credential
PS C:\> $session = Connect-openUBMC -Address 192.168.1.1 -Credential $credential -TrustCert
PS C:\> $pwd = ConvertTo-SecureString -String old-user-password -AsPlainText -Force
PS C:\> Add-openUBMCUser $session powershell $pwd 'Administrator'
PS C:\> $newPwd = ConvertTo-SecureString -String new-user-password -AsPlainText -Force
PS C:\> $User = Set-openUBMCUser -Session $session -Username powershell -NewUsername powershell2 -NewPassword $newPwd -NewRole Operator -Enabled $true -Unlocked $true
PS C:\> $User

Host     : 192.168.1.1
Id       : 12
Name     : User Account
UserName : powershell
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.EXAMPLE

Create a user account with name powershell and then modify the "username", "password", "role" properties of this user for multiple openUBMC servers

PS C:\> $credential = Get-Credential
PS C:\> $sessions = Connect-openUBMC -Address 192.168.1.1,192.168.10.4 -Credential $credential -TrustCert
PS C:\> $pwd = ConvertTo-SecureString -String old-user-password -AsPlainText -Force
PS C:\> Add-openUBMCUser -Session $sessions powershell $pwd 'Administrator'
PS C:\> $newPwd = ConvertTo-SecureString -String new-user-password -AsPlainText -Force
PS C:\> Set-openUBMCUser -Session $sessions -Username powershell -NewUsername powershell2 -NewPassword $newPwd -NewRole Operator

Host     : 192.168.1.1
Id       : 12
Name     : User Account
UserName : powershell
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.EXAMPLE

Modify "username", "password", "role" properties of a user with name "username" for multiple openUBMC servers

PS C:\> $credential = Get-Credential
PS C:\> $sessions = Connect-openUBMC -Address 192.168.1.1-3 -Credential $credential -TrustCert
PS C:\> $newPwd = ConvertTo-SecureString -String new-user-password -AsPlainText -Force
PS C:\> ,$sessions | Set-openUBMCUser -Username username -NewUsername new-user2 -NewPassword $newPwd -NewRole Administrator

Host     : 192.168.1.1
Id       : 12
Name     : User Account
UserName : new-user2
RoleId   : Administrator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

Host     : 192.168.1.2
Id       : 13
Name     : User Account
UserName : new-user2
RoleId   : Administrator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

Host     : 192.168.1.3
Id       : 14
Name     : User Account
UserName : new-user2
RoleId   : Administrator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.EXAMPLE

Modify "username", "password" properties of a user with name "username" with SecureEnabled on

PS C:\> $credential = Get-Credential
PS C:\> $sessions = Connect-openUBMC -Address 192.168.1.1 -Credential $credential -TrustCert
PS C:\> Set-openUBMCUser -Session $session -Username username -SecureEnabled

Host     : 192.168.1.1
Id       : 12
Name     : User Account
UserName : powershell
RoleId   : Operator
Locked   : True
Enabled  : True
Oem      : @{openUBMC=}

.LINK
https://gitcode.com/openUBMC/cmdlets_plugin

Add-openUBMCUser
Get-openUBMCUser
Remove-openUBMCUser
Connect-openUBMC
Disconnect-openUBMC

#>
  [CmdletBinding()]
  param (
    [RedfishSession[]]
    [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position=0)]
    $Session,

    [string[]]
    [parameter(Mandatory = $true)]
    $Username,

    [string[]]
    [parameter(Mandatory = $false)]
    $NewUsername,

    [System.Object[]]
    [parameter(Mandatory = $false)]
    $NewPassword,

    [UserRole[]]
    [parameter(Mandatory = $false)]
    $NewRole,

    [Boolean[]]
    [parameter(Mandatory = $false)]
    $Enabled,

    [switch[]]
    [parameter(Mandatory = $false)]
    $Unlocked,
    
    [switch]
    [parameter(Mandatory = $false)]
    $SecureEnabled
  )

  begin {
  }

  process {
    Assert-ArrayNotNull $Session 'Session'
    Assert-ArrayNotNull $Username 'Username'
    $Usernames = Get-MatchedSizeArray $Session $Username 'Session' 'Username'
    if ($SecureEnabled) {
      $Account = Get-AccountInfo
      $NewUsername = @($Account[0])
      $NewPassword = @($Account[1])
    }
    $NewUsernames = Get-OptionalMatchedSizeArray $Session $NewUsername
    $NewPasswords = Get-OptionalMatchedSizeArray $Session $NewPassword
    $NewRoles = Get-OptionalMatchedSizeArray $Session $NewRole
    $Enableds = Get-OptionalMatchedSizeArray $Session $Enabled
    $Unlockeds = Get-OptionalMatchedSizeArray $Session $Unlocked

    $SetUserBlock = {
      param($Session, $Username, $Payload)
      # try load all users
      $Users = Invoke-RedfishRequest $Session '/AccountService/Accounts' | ConvertFrom-WebResponse
      $found = $false
      for ($idx=0; $idx -lt $Users.Members.Count; $idx++) {
        $Member = $Users.Members[$idx]
        $UserResponse = Invoke-RedfishRequest $session $Member.'@odata.id'
        $User = $UserResponse | ConvertFrom-WebResponse
        if ($User.UserName -eq $Username) {
          $found = $true
          # Update user with provided $Username
          $Logger.info($(Trace-Session $Session "User $($User.UserName) found, will patch user now"))
          $Headers = @{'If-Match'=$UserResponse.Headers['Etag'];}
          # $Logger.info($(Trace-Session $Session "User Etag is $($UserResponse.Headers['Etag'])"))

          $Clone = $Payload.clone()
          if ($null -ne $Payload.Password) {
            $PlainPasswd = ConvertTo-PlainString $Payload.Password "NewPassword"
            $Payload.Password = $PlainPasswd
            $Clone.Password = "******"
          }

          $Logger.info($(Trace-Session $Session "Sending payload: $($Clone | ConvertTo-Json)"))
          $Response = Invoke-RedfishRequest $Session $User.'@odata.id' 'Patch' $Payload $Headers
          # $response = Invoke-RedfishRequest $Session '/AccountService/Accounts' 'Post' $payload -ContinueEvenFailed
          $SetUser = Resolve-RedfishPartialSuccessResponse $Session $Response
          $Properties = @("Id", "Name", "UserName", "RoleId", "Locked", "Enabled", "Oem")
          $PrettyUser = Copy-ObjectProperties $SetUser $Properties
          return $(Update-SessionAddress $Session $PrettyUser)
        }
      }

      if (-not $found) {
        throw $([string]::Format($(Get-i18n FAIL_NO_USER_WITH_NAME_EXISTS), $Username))
      }
    }

    try {
      $tasks = New-Object System.Collections.ArrayList
      $pool = New-RunspacePool $Session.Count
      for ($idx=0; $idx -lt $Session.Count; $idx++) {
        $Payload = @{
          "UserName"= $NewUsernames[$idx];
          "Password"= $NewPasswords[$idx];
          "RoleId"= $NewRoles[$idx];
          "Enabled"= $Enableds[$idx];
        } | Resolve-EnumValues

        $Playload_ = Remove-EmptyValues $Payload
        if ($Unlockeds[$idx] -eq $true) {
          $Playload_.Locked = $false
        }

        if ($null -eq $Playload_ -or $Playload_.Keys.Count -eq 0) {
          throw $(Get-i18n FAIL_NO_UPDATE_PARAMETER)
        } else {
          $Parameters = @($Session[$idx], $Usernames[$idx], $Playload_)
          [Void] $tasks.Add($(Start-ScriptBlockThread $pool $SetUserBlock $Parameters))
        }
      }
      return Get-AsyncTaskResults -AsyncTasks $tasks
    }
    finally {
      Close-Pool $pool
    }
  }

  end {
  }
}

function Remove-openUBMCUser {
<#
.SYNOPSIS
Remove an existing openUBMC user account.

.DESCRIPTION
Remove an existing openUBMC user account identified by "Username" parameter

.PARAMETER Session
openUBMC redfish session object which is created by Connect-openUBMC cmdlet.
A session object identifies an openUBMC server to which this cmdlet will be executed.

.PARAMETER Username
Username specifies the user to be removed.

.OUTPUTS
None
Nothing returned if cmdlet executes successfully.
In case of an error or warning, exception will be returned.

.EXAMPLE

Remove a openUBMC user account that has a username "user1"

PS C:\> $credential = Get-Credential
PS C:\> $session = Connect-openUBMC -Address 192.168.1.1 -Credential $credential -TrustCert
PS C:\> Remove-openUBMCUser -Session $session -Username user1


.EXAMPLE

Remove a openUBMC user account that has a username "user1"

PS C:\> $credential = Get-Credential
PS C:\> $session = Connect-openUBMC -Address 192.168.1.1 -Credential $credential -TrustCert
PS C:\> ,$session | Remove-openUBMCUser -Username user1


.LINK
https://gitcode.com/openUBMC/cmdlets_plugin

Add-openUBMCUser
Get-openUBMCUser
Set-openUBMCUser
Connect-openUBMC
Disconnect-openUBMC

#>
  [CmdletBinding()]
  param (
    [RedfishSession[]]
    [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position=0)]
    $Session,

    [string[]]
    [parameter(Mandatory = $true, Position=1)]
    $Username
  )

  begin {
  }

  process {
    Assert-ArrayNotNull $Session 'Session'
    Assert-ArrayNotNull $Username 'Username'
    $UsernameList = Get-MatchedSizeArray $Session $Username 'Session' 'Username'

    $DeleteUserBlock = {
      param($Session, $Username)
      # try load all users
      $Users = Invoke-RedfishRequest $Session '/AccountService/Accounts' | ConvertFrom-WebResponse
      $success = $false
      for ($idx=0; $idx -lt $Users.Members.Count; $idx++) {
        $Member = $Users.Members[$idx]
        $User = Invoke-RedfishRequest $session $Member.'@odata.id' | ConvertFrom-WebResponse
        if ($User.UserName -eq $Username) {
          # delete user with provided $Username
          $Logger.info($(Trace-Session $Session "User found, Delete User $($User.UserName) now"))
          Invoke-RedfishRequest $Session $User.'@odata.id' 'Delete' | Out-null
          $Logger.info($(Trace-Session $Session "Delete User $($User.UserName) succeed"))
          $success = $true
          break
        }
      }

      if (-not $success) {
        throw $([string]::Format($(Get-i18n FAIL_NO_USER_WITH_NAME_EXISTS), $Username))
      } else {
        return $null
      }
    }

    try {
      $tasks = New-Object System.Collections.ArrayList
      $pool = New-RunspacePool $Session.Count
      for ($idx=0; $idx -lt $Session.Count; $idx++) {
        $parameter = @($Session[$idx], $UsernameList[$idx])
        [Void] $tasks.Add($(Start-ScriptBlockThread $pool $DeleteUserBlock $parameter))
      }
      return Get-AsyncTaskResults -AsyncTasks $tasks
    } finally {
      Close-Pool $pool
    }
  }

  end {
  }
}